﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using Microsoft.Surface.Presentation.Controls;
using System.Windows;
using System.Windows.Controls;

using SurfaceCardsBoardLibrary.Interfaces;

namespace SurfaceCardsBoardLibrary
{
    public struct ScatterViewItemCollision
    {
        private Geometry _geometry;
        private IEnumerable<ScatterViewItem> _collides;

        public ScatterViewItemCollision(Geometry geometry, IEnumerable<ScatterViewItem> collides)
        {
            _geometry = geometry;
            _collides = collides;
        }

        public Geometry Geometry
        {
            get
            {
                return _geometry;
            }
        }
        
        public IEnumerable<ScatterViewItem> Collides
        {
            get
            {
                return _collides;
            }
        }

        public Point Center
        {
            get
            {
                Point p = new Point();

                p.X = Geometry.Bounds.Location.X + Geometry.Bounds.Size.Width / 2.0;
                p.Y = Geometry.Bounds.Location.Y + Geometry.Bounds.Size.Height / 2.0;

                return p;
            }
        }

        public bool HasCollision
        {
            get
            {
                return Collides != null && Collides.Any();
            }
        }
    }

    /// <summary>
    /// Surface generic helper
    /// </summary>
    public static class SurfaceHelper
    {
        /// <summary>
        /// Creates the geometry from a ScatterViewItem.
        /// </summary>
        /// <param name="item">The ScatterViewItem from which to create the geometry.</param>
        /// <returns>Returns the geometry of the ScatterViewItem.</returns>
        /// <remarks>The geometry returned is frozen.</remarks>
        public static Geometry ComputeScatterViewGeometry(ScatterViewItem item)
        {
            var rect = new Rect(0, 0, item.ActualWidth, item.ActualHeight);

            rect.Offset(-item.ActualWidth / 2, -item.ActualHeight / 2);
            var tansformGroup = new TransformGroup();

            tansformGroup.Children.Add(new RotateTransform(item.ActualOrientation));
            tansformGroup.Children.Add(new TranslateTransform(item.ActualCenter.X, item.ActualCenter.Y));

            var geometry = new RectangleGeometry(rect, 0, 0, tansformGroup);
            geometry.Freeze();

            return geometry;
        }

        /// <summary>
        /// Looks for all the ScatterViewItems in the same ScatterView as <paramref name="source"/> that collides with <paramref name="source"/>.
        /// </summary>
        /// <param name="source">The ScatterViewItem for which to check the collisions.</param>
        /// <returns>Returns a collection of all the ScatterViewItems colliding with <paramref name="source"/>.</returns>
        public static IEnumerable<ScatterViewItem> GetOverridingScatterViewItems(ScatterViewItem source)
        {
            var scatterview = ItemsControl.ItemsControlFromItemContainer(source) as ScatterView;

            var list = scatterview.Items
                .OfType<object>()
                .Select(it => scatterview.ItemContainerGenerator.ContainerFromItem(it) as ScatterViewItem);

            return GetOverridingScatterViewItems(source, list);
        }

        public static ScatterViewItemCollision GetOverridingScatterViewItemsVerbose(ScatterViewItem source)
        {
            var scatterview = ItemsControl.ItemsControlFromItemContainer(source) as ScatterView;

            var list = scatterview.Items
                .OfType<object>()
                .Select(it => scatterview.ItemContainerGenerator.ContainerFromItem(it) as ScatterViewItem);

            return GetOverridingScatterViewItemsVerbose(source, list);
        }

        /// <summary>
        /// Looks for all the ScatterViewItems that collides with <paramref name="source"/> in <paramref name="list"/>.
        /// </summary>
        /// <param name="source">The ScatterViewItem for which to check the collisions.</param>
        /// <param name="list">The list of ScatterViewItems from which to check the collisions.</param>
        /// <returns>Returns a collection of all the ScatterViewItems colliding with <paramref name="source"/>.</returns>
        public static IEnumerable<ScatterViewItem> GetOverridingScatterViewItems(ScatterViewItem source, IEnumerable<ScatterViewItem> list)
        {
            var collides = new List<ScatterViewItem>();

            var geometry_source = ComputeScatterViewGeometry(source);
            foreach (ScatterViewItem svi in list)
            {
                if (svi == source)
                    continue;

                var geometry = ComputeScatterViewGeometry(svi);

                var intersect = geometry_source.FillContainsWithDetail(geometry);

                if (IsColliding(intersect))
                    collides.Add(svi);
            }

            return collides;
        }

        public static ScatterViewItemCollision GetOverridingScatterViewItemsVerbose(ScatterViewItem source, IEnumerable<ScatterViewItem> list)
        {
            var collides = new List<ScatterViewItem>();

            var geometry_source = ComputeScatterViewGeometry(source);
            var geometry_return = Geometry.Empty;

            foreach (ScatterViewItem svi in list)
            {
                if (svi == source)
                    continue;

                var geometry = ComputeScatterViewGeometry(svi);
                var intersect = geometry_source.FillContainsWithDetail(geometry);

                if (IsColliding(intersect))
                {
                    if (svi.IsDeck() || !svi.IsDeckCard())
                        geometry_return = Geometry.Combine(geometry_return, geometry, GeometryCombineMode.Union, null);
                    collides.Add(svi);
                }
            }

            return new ScatterViewItemCollision(geometry_return, collides);
        }

        /// <summary>
        /// Tests whether two ScatterViewItems collides or not.
        /// </summary>
        /// <param name="item1">First item.</param>
        /// <param name="item2">Second item.</param>
        /// <returns>Returns true if <paramref name="item1"/> and <paramref name="item2"/> collides.</returns>
        public static bool TestScatterViewCollision(ScatterViewItem item1, ScatterViewItem item2)
        {
            var scatterview1 = ItemsControl.ItemsControlFromItemContainer(item1) as ScatterView;
            var scatterview2 = ItemsControl.ItemsControlFromItemContainer(item2) as ScatterView;

            if (scatterview1 != scatterview2)
                return false;

            var geometry1 = ComputeScatterViewGeometry(item1);
            var geometry2 = ComputeScatterViewGeometry(item2);

            var intersect = geometry1.FillContainsWithDetail(geometry2);

            return IsColliding(intersect);
        }

        /// <summary>
        /// Test if the result of the intersection operation is a collision.
        /// </summary>
        /// <param name="intersect">Result of the intersection operation</param>
        /// <returns>True if the results of the intersection indicates a collision.</returns>
        public static bool IsColliding(IntersectionDetail intersect)
        {
            return (int)intersect > (int)IntersectionDetail.Empty;
        }
    }
}
